package com.onepiece.shipelves.common.sort;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.Transient;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 提取双层排序抽象类
 *
 * @author guoyanjiang
 * @date 2019-01-28
 */
public abstract class AbstractMultiLevelSortVO<T extends BaseCompareBean> {

    Logger logger = LoggerFactory.getLogger(getClass());

    private static final String ASC = "asc";
    private static final String SPLIT_POINT = ".";
    private static final String REGEX_POINT = "\\.";

    @Transient
    public abstract List<T> provideSortList();

    @Transient
    public abstract void updateSortList(List<T> list);

    @Transient
    public List<T> doOrderOrPaging(String orderField, String orderType) {
        return doOrderOrPaging(new DefaultBasePageReq(orderField, orderType));
    }

    @Transient
    public List<T> doOrderOrPaging(Integer page, Integer size, String orderField, String orderType) {
        return doOrderOrPaging(new DefaultBasePageReq(page, size, orderField, orderType));
    }

    //todo 排序代码需要抽出公共方法 ps 不但需要抽出公共方法，排序的Bean框架最好能抽离，可以做到需要排序的VO只要实现基类中的抽象方法即可
    @Transient
    public List<T> doOrderOrPaging(BasePageReq req) {
        String orderField = req.getOrderField();
        String orderType = req.getOrderType();
        //todo 业务代码写完后在尝试
        Stream<T> tickerStream = provideSortList().stream();

        if (req.needOrder()) {
            Comparator<T> comparator = (o1, o2) -> doCompare(orderField, o1, o2, ASC.equalsIgnoreCase(orderType));
            if (!ASC.equalsIgnoreCase(orderType)) {
                comparator = comparator.reversed();
            }
            tickerStream = tickerStream.sorted(comparator);
        }

        if (req.needPaging()) {
            Integer pageSize = req.checkAndGetDefaultPageSize();
            Integer pageNum = req.checkAndGetDefaultPage();
            tickerStream = tickerStream.skip(pageSize * (pageNum - 1)).limit(pageSize);
        }
        List<T> list = tickerStream.collect(Collectors.toList());
        updateSortList(list);
        return list;
    }

    @Transient
    public int doCompare(String orderField, T o1, T o2, boolean asc) {
        try {
            /*
                空边界之一
                先解决bean为空的情况，原则：无论升序还是降序，空bean无需比较，直接往后放
             */
            if (o1 == null || o1.abnormal()) {
                return thisValuePutBehind(asc);
            }
            if (o2 == null || o2.abnormal()) {
                return thisValuePutForward(asc);
            }

            Comparable v1 = null;
            Comparable v2 = null;
            if (orderField.contains(SPLIT_POINT)) {
                //说明要用第二层数据排序
                String[] fields = orderField.split(REGEX_POINT);
                v1 = extractTwoLayerComparableValue(o1, fields);
                v2 = extractTwoLayerComparableValue(o2, fields);
            } else {
                //直接取PairInfo类里的字段
                v1 = o1.tryBestToFindCompareValue(orderField);
                v2 = o2.tryBestToFindCompareValue(orderField);
            }
            return compareThinkAboutNull(v1, v2, asc);
        } catch (Exception e) {
            logger.error("获取指定比较字段失败，放弃比较。 orderField {} e {}", orderField, ExceptionUtils.getFullStackTrace(e));
            //若仍有意外发生，也先往后放
            return thisValuePutBehind(asc);
        }
    }

    private Comparable extractTwoLayerComparableValue(T obj, String[] fields) {
        BaseCompareBean bean = obj.tryBestToFindFieldValue(fields[0], BaseCompareBean.class);
        if (bean != null) {
            return bean.tryBestToFindCompareValue(fields[1]);
        }
        return null;
    }

    /**
     * 根据排序方式 把当前值往前放
     *
     * @param asc
     * @return
     */
    private int thisValuePutForward(boolean asc) {
        return asc ? -1 : 1;
    }

    /**
     * 根据排序方式 把当前值往后放
     *
     * @param asc
     * @return
     */
    private int thisValuePutBehind(boolean asc) {
        return asc ? 1 : -1;
    }

    private int compareThinkAboutNull(Comparable v1, Comparable v2, boolean asc) {
        /*
            空边界之二
            解决最后真正比较字段的空问题，原则相同
         */
        if (v1 == null) {
            return thisValuePutBehind(asc);
        }
        if (v2 == null) {
            return thisValuePutForward(asc);
        }
        return v1.compareTo(v2);
    }

    class DefaultBasePageReq extends AbstractBasePageReq {

        public DefaultBasePageReq(String orderField, String orderType) {
            super(orderField, orderType);
        }

        public DefaultBasePageReq(Integer page, Integer count, String orderField, String orderType) {
            super(page, count, orderField, orderType);
        }
    }


}
